package momo.multitree.simulation;

import java.io.File;
import java.io.FileReader;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import momo.multitree.structure.Edge;
import momo.multitree.structure.Graph;
import momo.multitree.structure.Node;
import momo.multitree.structure.Tree;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class LatCostOptTreeFrameWork {
	
	private Log log = LogFactory.getLog(LatCostOptTreeFrameWork.class);
	private final int maxIteration = 100;
	private final int maxTrial = 100;
	private final double temperature = 0d;
	private final double factor = 0.98d;
	
	private int sizes[] = {30};
	private String[] filenames;
//	private String[] filenames = {"30_node_symmetric_noleaf_dataset_15.txt"};
	
	
	private long seed = 345768652;
	private Random rand = new Random(seed);
	
	public LatCostOptTreeFrameWork() {
		filenames = new String[ sizes.length*100 ];
		int counter = 0;
		for(int i=0; i<sizes.length; i++)
		{
			for(int j=0; j<100; j++) {
//				P.S: doesn't work on normal graph. Booted nodes that has leaf edges, will have those nodes booted out as well
				filenames[ j + (counter*100) ] = sizes[i]+"_node_symmetric_noleaf_dataset_" + (j+1) +".txt";
			}
			counter ++;
		}	
	}
	
	public void run()
	{
		for(String filename: filenames)
		{
			try
			{
				Graph saGraph = new Graph(new FileReader("dataset/"+filename));
				Graph algoGraph = new Graph(new FileReader("dataset/"+filename));
				
				SimulatedAnnealing sim = new SimulatedAnnealing(saGraph, maxIteration, maxTrial, temperature, factor);
				Tree saTree = sim.optimizeTree();
				
				LatCostOptTree algo = new LatCostOptTree(algoGraph, maxIteration, maxTrial, temperature, factor);
				Tree algoTree = algo.optimizeTree();
				
//				log.info(filename);
//				
//				File dir = new File("visual/TreeRender/test");
//				for ( File f : dir.listFiles() )
//					if ( f.getName().endsWith(".svg") )
//					f.delete();
//				
//				saTree.outputVisual("test/sa_tree");
//				algoTree.outputVisual("test/algo_tree");
				
				log.info(
						filename + "," +
						algo.score(saTree) + "," +
						algo.score(algoTree) + ","
						);
				
				for ( int i=0; i<5; i++ ) {
					String bootedNodeId = getBootedNodeId(saTree, algoTree);
//					log.info("bootedNode:" + bootedNodeId);
					restructure(bootedNodeId, saTree, saGraph);
					restructure(bootedNodeId, algoTree, algoGraph);
//					saTree.outputVisual("test/sa_tree_after_" + i);
//					algoTree.outputVisual("test/algo_tree_after_" + i);
					
					log.info(
							filename + "," +
							algo.score(saTree) + "," +
							algo.score(algoTree) + ","
							);
				}
			}catch(Exception e)
			{
				log.error("Error on filename: " + filename);
				e.printStackTrace();
				System.exit(0);
			}
		}
	}
	
	private String getBootedNodeId(Tree t, Tree a) {
		double prob = rand.nextDouble();
		double sum = 0;
		double currCum = 0;
		Map<String, Double> map = new LinkedHashMap<String, Double>();
		Set<Node> set = new LinkedHashSet<Node>(t.getNodes());
		set.remove( t.getRoot() );
		set.remove( a.getRoot() );
		
		for( Node n : set ) {
			double stab = n.getStability();
			sum += stab;
			map.put(n.getId(), stab);
		}
		for( String nodeId : map.keySet() ) {
			currCum += map.get(nodeId)/sum;
			if ( prob <= currCum )
				return nodeId;
		}
		throw new RuntimeException("No Booted Node Id found");
	}
	
	private void restructure(String bootedNodeId, Tree t, Graph g) {
		Node bootedNode = t.getNode(bootedNodeId);
		Node parentNode = t.getParent(bootedNode);
		Set<Node> children = t.getChildren(bootedNode);
		Set<Node> remainingNodes = new LinkedHashSet<Node>(t.getNodes());
		remainingNodes.removeAll( t.getDescendants(bootedNode) );
		remainingNodes.remove( bootedNode );
		
//		for (Node n: children)
//			log.info("Child of booteNode: " + n.getId());
		
		t.remove( bootedNode );
		for ( Node n : children ) {
			Edge e;
			boolean success = false;
			if ( (e=g.getEdge(n, parentNode)) != null ) {
//				log.info("Parent join: "+n.getId() +", " + parentNode);
				t.add(e);
				success = true;
			}else {
				for ( Node r : remainingNodes ) {
					if ( (e=g.getEdge(n, r)) != null ) {
//						log.info("Remaining join: "+n.getId() +", " + r.getId());
						t.add(e);
						success = true;
						break;
					}
				}
			}
			if ( !success )
				throw new RuntimeException("Node " + n.getId() + " doesnt join onto any nodes");
		}
	}
	
	public static void main(String args[]) {
		LatCostOptTreeFrameWork app = new LatCostOptTreeFrameWork();
		app.run();
	}
	
}//end of class LatCostOptTreeFrameWork
